package com.yz.order.mq;

import com.alibaba.fastjson.JSON;
import com.rabbitmq.client.Channel;
import com.yz.common.constant.MQConstants;
import com.yz.common.resp.BaseResponse;
import com.yz.model.entity.order.Order;
import com.yz.order.service.OrderService;
import com.yz.order.service.feign.OrderPromotionFeign;
import com.yz.model.entity.promotion.PromotionCode;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

/**
 * Created With IntelliJ IDEA.
 * Descriptions:
 * <p>
 * User:Mr.Du
 * Date:2024/4/9
 * Time:13:43
 */
@Slf4j
@Component
public class PromotionMessageConsumer {

    @Resource
    private OrderPromotionFeign orderPromotionFeign;

    @Resource
    private OrderService orderService;

    private Integer MAX_RETRY_COUNT = 3;

    private Integer cnt = 0;

    @SneakyThrows //
    @RabbitListener(queues = {MQConstants.PROMOTION_QUEUE_NAME}, ackMode = "MANUAL")
    public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
        try{
            this.handleMessage(message, channel, deliveryTag);
        }catch (Exception e){
            log.error("消息消费失败promotion", e.getMessage());
            if(++cnt > MAX_RETRY_COUNT){
                channel.basicNack(deliveryTag, false, false);
            }else{
                channel.basicNack(deliveryTag, false, true);
            }
        }
    }

    /**
     * 处理消息，尝试使用订单中的优惠码。
     * @param message 包含订单号的字符串消息。
     * @param channel AMQP消息通道，用于确认或拒绝消息处理。
     * @param deliveryTag 消息的唯一标识，用于确认或拒绝消息。
     * @throws IOException 当与消息队列的通信发生错误时抛出。
     */
    @Transactional
    void handleMessage(String message, Channel channel, long deliveryTag) throws IOException {
        log.info("receiveMessage message = {}", message);
        // 检查消息是否为空，若为空则拒绝处理并抛出异常
        if(StringUtils.isBlank(message)){
            channel.basicNack(deliveryTag, false, false);
            throw new RuntimeException("消息为空");
        }
        // 尝试解析消息为订单号
        long tradeNo = Long.parseLong(message);
        Order order = orderService.getById(tradeNo);
        // 如果订单不存在，则拒绝处理并抛出异常
        if(order == null){
            channel.basicNack(deliveryTag, false, false);
            throw new RuntimeException("订单不存在");
        }
        // 尝试从订单中解析优惠码
        PromotionCode promotionCode = JSON.parseObject(order.getPromotionCode(), PromotionCode.class);
        log.info("promotionCode = {}", promotionCode);
        // 如果优惠码为空，则确认消息处理并直接返回
        if(promotionCode.getCode() == null){
            log.info("优惠码为空: " + null);
            channel.basicAck(deliveryTag, false);
            return;
        }
        // 组合优惠码信息
        String codes = promotionCode.getCode() + promotionCode.getToken() + promotionCode.getType();
        // 尝试使用优惠码
        BaseResponse<Boolean> booleanBaseResponse = orderPromotionFeign.usePromotionCode(order.getUid(), codes);
        // 如果优惠码使用失败，则拒绝处理并抛出异常
        if(booleanBaseResponse.getCode() != 0){
            log.info("优惠码使用失败: " + codes);
            channel.basicNack(deliveryTag, false, false);
            throw new RuntimeException("优惠码使用失败");
        }
        // 优惠码使用成功，确认消息处理
        channel.basicAck(deliveryTag, false);
    }

}